using System;
using System.Collections;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;

namespace MetaWorld
{


	/// <summary>
	/// SceneGraphNode is an element of the scene graph. 
	/// It includes transformation, material, texture info, and 
	/// optional collections of leaf stubs. Children of SceneGraphNode
	/// can also extend it to add more characteristics.
	/// 
	/// At initialization time, each node's Begin function gets called recursively,
	/// in order to initialize all nodes.
	/// 
	/// At every frame, each node's properties are applied to the scene,
	/// its RenderSelf function is called to perform rendering, and children
	/// are called recursively.
	/// 
	/// After rendering, each node's End function gets called recursively 
	/// to perform any cleanup necessary.
	/// 
	/// Notice that each node contains Meta procedures that compute color and 
	/// position information at every frame. If a given procedure is not specified,
	/// the parent's information is inherited.
	/// </summary>
	public class SceneGraphNode 
	{
		/// <summary>
		/// Reference to the Scene object
		/// </summary>
		public Scene Scene;

		/// <summary>
		/// Meta procedure that will compute object's dynamic 
		/// position at every frame.
		/// </summary>
		public Meta.Procedure Translation, Rotation, Scaling;

		/// <summary>
		/// Meta procedure that will compute object's dynamic 
		/// color at every frame.
		/// </summary>
		public Meta.Procedure ColorDiffuse, ColorAmbient, ColorEmissive;

		/// <summary>
		/// Optional Meta procedure that will be called before every frame
		/// and before position and color calculations; can be used for side effects, 
		/// such as adjusting scene or display characteristics.
		/// </summary>
		public Meta.Procedure SideEffect;

		/// <summary>
		/// Internal: storage for pixel shader information, if applicable
		/// </summary>
		protected string pixelShaderSource;

		/// <summary>
		/// Internal: storage for an instance of a pixel shader, if applicable
		/// </summary>
		protected Effect pixelShaderEffect;

		/// <summary>
		/// Internal: name of the currently used pixel shader technique, if applicable
		/// </summary>
		protected string pixelShaderTechniqueName;

		/// <summary>
		/// Internal: flag specifies whether we're using a pixel shader
		/// </summary>
		protected bool usingPixelShader;

		/// <summary>
		/// Internal: collection of children nodes and leaves
		/// </summary>
		protected ArrayList children;

		/// <summary>
		/// Internal: version of the pixel shared required for this node. If the hardware
		/// does not meet the minimal version, resort to software rendering.
		/// </summary>
		protected Version pixelShaderRequiredVersion;

		/// <summary>
		/// Internal: version of the vertex shared required for this node. If the hardware
		/// does not meet the minimal version, resort to software rendering.
		/// </summary>
		protected Version vertexShaderRequiredVersion;

		/// <summary>
		/// Returns the highest pixel shader version required by this node and all its children
		/// </summary>
		public Version PixelShaderRequiredVersion 
		{
			get 
			{ 
				Version v = this.pixelShaderRequiredVersion;
				foreach (SceneGraphNode node in this.children) 
				{
					Version cv = node.PixelShaderRequiredVersion;
					if (v < cv) v = cv;
				}
				return v;
			}
		}

		/// <summary>
		/// Returns the highest vertex shader version required by this node and all its children
		/// </summary>
		public Version VertexShaderRequiredVersion 
		{
			get 
			{ 
				Version v = this.vertexShaderRequiredVersion;
				foreach (SceneGraphNode node in this.children) 
				{
					Version cv = node.VertexShaderRequiredVersion;
					if (v < cv) v = cv;
				}
				return v;
			}
		}

		/// <summary>
		/// Default constructor, initializes default values.
		/// </summary>
		public SceneGraphNode ()
		{
			this.children = new ArrayList ();
			this.usingPixelShader = false;
			this.pixelShaderEffect = null;
			this.pixelShaderRequiredVersion = this.vertexShaderRequiredVersion = new Version (0, 0);
		}

		/// <summary>
		/// Constructor that accepts a list of child nodes
		/// </summary>
		/// <param name="children"></param>
		public SceneGraphNode (ArrayList children) : base ()
		{
			// let's manually iterate to ensure type
			foreach (SceneGraphNode stub in children) {	Add (stub); }
		}

		/// <summary>
		/// Adds a new node into the scene graph. 
		/// </summary>
		/// <param name="item"></param>
		public void Add (SceneGraphNode item)
		{
			children.Add (item);
		}


		/// <summary>
		/// Compute a matrix corresponding to all of the transformations 
		/// specified for this node. Uses position thunks if those were defined.
		/// </summary>
		public Matrix UpdatePositions ()
		{
			Matrix Transforms = Matrix.Identity;

			if (this.Scaling != null) { 
				Vector3 v = (Vector3) this.Scaling.Call ();
				Matrix Scaling = Matrix.Scaling (v);
				Transforms = Scaling;
			}

			if (this.Rotation != null) 
			{ 
				Vector3 v = (Vector3) this.Rotation.Call (); 
				Matrix Rotation = Matrix.RotationYawPitchRoll (v.Y, v.X, v.Z);
				Transforms = Transforms * Rotation;
			}
				
			if (this.Translation != null) 
			{ 
				Vector3 v = (Vector3) this.Translation.Call ();
				Matrix Translation = Matrix.Translation (v);
				Transforms = Transforms * Translation;
			}

			return Transforms;
		}

		/// <summary>
		/// Compute a material structure describing the object's color properties.
		/// Uses color thunks if those were defined.
		/// </summary>
		/// <returns></returns>
		public Material UpdateColors ()
		{
			Material Material = Scene.Device.Material;

			if (this.ColorDiffuse != null) { Material.Diffuse = (Color) this.ColorDiffuse.Call (); }
			if (this.ColorAmbient != null) { Material.Ambient = (Color) this.ColorAmbient.Call (); }
			if (this.ColorEmissive != null) { Material.Emissive = (Color) this.ColorEmissive.Call (); }

			return Material;
		}


		/// <summary>
		/// Set pixel shader information
		/// </summary>
		/// <param name="SourceFile"></param>
		/// <param name="TechniqueName"></param>
		public void SetPixelShader (string SourceFile, string TechniqueName)
		{
			this.pixelShaderSource = SourceFile;
			this.pixelShaderTechniqueName = TechniqueName;

			usingPixelShader = true;
			this.pixelShaderRequiredVersion = new Version (1, 1);
			this.vertexShaderRequiredVersion = new Version (1, 1);
		}

		/// <summary>
		/// Tells the texture pixel shader which technique to use
		/// out of all defined in the specified effects file
		/// </summary>
		/// <param name="TechniqueName"></param>
		public void SetPixelShaderTechnique (string TechniqueName)
		{
			this.pixelShaderTechniqueName = TechniqueName;
		}


		/* RUN-TIME FUNCTIONALITY */

		/// <summary>
		/// Virtual function that initializes scene-specific data 
		/// (meshes, effects, etc.)
		/// </summary>
		/// <param name="scene"></param>
		virtual public void Begin (Scene scene)
		{
			this.Scene = scene;
			if (this.pixelShaderSource != null && this.pixelShaderSource != "")
			{
				pixelShaderEffect = 
					Effect.FromString (Scene.Device, pixelShaderSource, 
										null, ShaderFlags.None, null);
				SetPixelShaderTechnique (pixelShaderTechniqueName);
			}

			// now recurse
			foreach (SceneGraphNode node in children) { node.Begin (scene); }
		}

		/// <summary>
		/// Virtual function that deinitializes scene-specific data.
		/// </summary>
		virtual public void End ()
		{
			this.Scene = null;
			this.pixelShaderEffect = null;

			// now recurse
			foreach (SceneGraphNode node in children) { node.End (); }
		}

		/// <summary>
		/// Virtual function that renders the node itself;
		/// to be overridden by specialized children.
		/// </summary>
		virtual public void RenderSelf ()
		{	
		}

		/// <summary>
		/// Render the node connected to this stub, by applying transforms, 
		/// material, and texture, and rendering itself and each child.
		/// </summary>
		public void Render ()
		{
			if (Scene == null) throw new Exception ("Scene needs to be set before a call to Render(). Call Begin() on the root of the scene graph node first!");

			// try the side effect
			if (this.SideEffect != null) this.SideEffect.Call ();

			// compute transforms and material for this node
			Matrix Transform = this.UpdatePositions ();
			Matrix TotalTransform = Transform * Scene.Device.Transform.World;
			Material Material = this.UpdateColors ();
			
			// apply transforms and render the node's own object (if applicable)
			Scene.Device.SetTexture (0, null);
			Scene.Device.Material = Material;
			Scene.Device.Transform.World = TotalTransform;
			RenderSelf ();

			// now do the same for each child!
			foreach (SceneGraphNode child in children)
			{
				Scene.Device.SetTexture (0, null);
				Scene.Device.Material = Material;
				Scene.Device.Transform.World = TotalTransform;
				child.Render ();
			}
		}

		

	
	};
	        
		
	

}

	
	